﻿// Autore: Alessio Parma

using System;
using System.Collections.Generic;
using System.Data.Objects.DataClasses;
using System.Linq;

namespace RecipesManager.Data.Implementation
{
    sealed partial class Ingredient : IIngredient
    {
        readonly ILink<ICategory> _categories;

        public Ingredient()
        {
            _categories = new CategoryEntities(() => Categories);
        }

        #region IIngredient Members

        IEnumerable<IGuest> IIngredient.GuestIntolerances
        {
            get { return GuestIntolerances; }
        }

        IEnumerable<IOtherRating<IGuest, FoodRating>> IIngredient.GuestRatings
        {
            get { return GuestRatings; }
        }

        MeasureType IIngredient.MeasureType
        {
            get { return (MeasureType) MeasureType; }
        }

        IEnumerable<IRecipe> IIngredient.OriginalRecipes
        {
            get { return OriginalRecipeQuantities.Select(r => r.Recipe); }
        }

        IEnumerable<IRecipe> IIngredient.Recipes
        {
            get { return RecipeQuantities.Select(r => r.Recipe); }
        }

        string INamedEntity.Name
        {
            get { return Name; }
        }

        IMeasure IIngredient.StockMeasure
        {
            get { return StockMeasure; }
        }

        double IIngredient.StockQuantity
        {
            get { return StockQuantity; }
        }

        ILink<ICategory> IIngredient.Categories
        {
            get { return _categories; }
        }

        IEnumerable<IOtherRating<IShop, ShopRating>> IIngredient.ShopRatings
        {
            get { return ShopRatings; }
        }

        public bool Equals(IIngredient other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return Id == other.Id;
        }

        public void Update(string name, MeasureType measureType, string stockMeasureName, double stockQuantity)
        {
            Name = name.ToKey();
            MeasureType = (byte) measureType;
            StockMeasure = (Measure) Manager.Measures[stockMeasureName];
            StockQuantity = stockQuantity;
            Manager.SaveChanges();
        }

        #endregion

        public static Ingredient New(string name, MeasureType type, string stockMeasureName, double stockQuantity)
        {
            name = name.ToKey();
            var t = (byte) type;
            var m = Manager.Measures[stockMeasureName].Id;
            return CreateIngredient(0, name, t, m, stockQuantity);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            return ReferenceEquals(this, obj) || Equals(obj as IIngredient);
        }

        public override int GetHashCode()
        {
            return Id;
        }

        public override string ToString()
        {
            return string.Format("Ingredient(Name: {0})", Name);
        }
    }

    sealed class IngredientObjects : NamedObjects<Ingredient, IIngredient>, IIngredients
    {
        public IngredientObjects() : base(Manager.IngredientSet)
        {
        }

        #region IIngredients Members

        public IIngredient Add(string name, MeasureType type, string stockMeasureName = "grammi",
                               double stockQuantity = 0.0)
        {
            return Add(Ingredient.New(name, type, stockMeasureName, stockQuantity));
        }

        #endregion
    }

    sealed class IngredientEntities : NamedEntities<Ingredient, IIngredient>, ILink<IIngredient>
    {
        public IngredientEntities(Func<EntityCollection<Ingredient>> items) : base(items, Manager.IngredientSet)
        {
        }

        #region ILink<IIngredient> Members

        public INamedEntities<IIngredient> Entities
        {
            get { return Manager.Ingredients; }
        }

        public IIngredient Add(string name)
        {
            return AddName(name);
        }

        #endregion
    }

    sealed partial class IngredientQuantity : IIngredientQuantity
    {
        #region IIngredientQuantity Members

        string IIngredientQuantity.FormattedQuantity
        {
            get { return "NOT USED"; }
        }

        IIngredient IIngredientQuantity.Ingredient
        {
            get { return Ingredient; }
        }

        int IEntity.Id
        {
            get { return IngredientId; }
        }

        IMeasure IIngredientQuantity.Measure
        {
            get { return Measure; }
        }

        string INamedEntity.Name
        {
            get { return Ingredient.Name; }
        }

        public void Update(string measureName, double quantity, string notes)
        {
            Measure = (Measure) Manager.Measures[measureName];
            Quantity = quantity;
            Notes = notes;
        }

        #endregion
    }

    sealed partial class OriginalIngredientQuantity : IIngredientQuantity
    {
        #region IIngredientQuantity Members

        string IIngredientQuantity.FormattedQuantity
        {
            get { return "NOT USED"; }
        }

        IIngredient IIngredientQuantity.Ingredient
        {
            get { return Ingredient; }
        }

        int IEntity.Id
        {
            get { return IngredientId; }
        }

        IMeasure IIngredientQuantity.Measure
        {
            get { return Measure; }
        }

        string INamedEntity.Name
        {
            get { return Ingredient.Name; }
        }

        public void Update(string measureName, double quantity, string notes)
        {
            Measure = (Measure) Manager.Measures[measureName];
            Quantity = quantity;
            Notes = notes;
        }

        #endregion

        public static OriginalIngredientQuantity New(int recipeId, IngredientQuantity iq)
        {
            return CreateOriginalIngredientQuantity(recipeId, iq.IngredientId, iq.MeasureId, iq.Quantity, iq.Notes);
        }
    }

    sealed class IngredientQuantities : NamedLinkSet<IngredientQuantity, IIngredientQuantity>, IIngredientQuantities
    {
        public IngredientQuantities(Func<EntityCollection<IngredientQuantity>> items, Func<int> idGetter)
            : base(items, idGetter)
        {
        }

        #region IIngredientQuantities Members

        public IIngredientQuantity Add(string name, string measureName, double quantity, string notes = "")
        {
            var i = Manager.Ingredients[name].Id;
            var m = Manager.Measures[measureName].Id;
            return Add(IngredientQuantity.CreateIngredientQuantity(IdGetter(), i, m, quantity, notes));
        }

        #endregion
    }

    sealed partial class IngredientGuestRating : IRating<IIngredient, FoodRating>, IOtherRating<IGuest, FoodRating>
    {
        #region IOtherRating<IGuest,FoodRating> Members

        int IOtherRating<IGuest, FoodRating>.Id
        {
            get { return GuestId; }
        }

        IGuest IOtherRating<IGuest, FoodRating>.Entity
        {
            get { return Guest; }
        }

        string IOtherRating<IGuest, FoodRating>.Name
        {
            get { return Guest.Name; }
        }

        FoodRating IOtherRating<IGuest, FoodRating>.Rating
        {
            get { return (FoodRating) Rating; }
        }

        #endregion

        #region IRating<IIngredient,FoodRating> Members

        IIngredient IRating<IIngredient, FoodRating>.Entity
        {
            get { return Ingredient; }
        }

        int IEntity.Id
        {
            get { return IngredientId; }
        }

        string INamedEntity.Name
        {
            get { return Ingredient.Name; }
        }

        FoodRating IRating<IIngredient, FoodRating>.Rating
        {
            get { return (FoodRating) Rating; }
        }

        public void Update(FoodRating rating)
        {
            Rating = (byte) rating;
            Manager.SaveChanges();
        }

        #endregion
    }

    sealed class IngredientRatings : NamedLinkSet<IngredientGuestRating, IRating<IIngredient, FoodRating>>,
                                     IRatings<IIngredient, FoodRating>
    {
        public IngredientRatings(Func<EntityCollection<IngredientGuestRating>> items, Func<int> idGetter)
            : base(items, idGetter)
        {
        }

        #region IRatings<IIngredient,FoodRating> Members

        public INamedEntities<IIngredient> Entities
        {
            get { return Manager.Ingredients; }
        }

        public IRating<IIngredient, FoodRating> Add(string name, FoodRating rating)
        {
            return Add(IngredientGuestRating.CreateIngredientGuestRating(IdGetter(), Entities[name].Id, (byte) rating));
        }

        #endregion
    }
}